﻿using System;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;

using FXnaEngine.Input;




namespace FXnaEngine.Components
{

    /// <summary>
    /// 用于编辑器系统，
    /// </summary>
    public class FEditorInputComponent:FInputComponent
    {

        public FEditorInputComponent(FEntity parent):base(parent)
        { 


        }


        public override void Initialize()
        {
            base.Initialize();


            // Listen for these mouse buttons, also only for example, this camera uses event-based input for mouse buttons
            this.inputs.AddInputListener(MouseButton.Left);
            this.inputs.AddInputListener(MouseButton.Right);

            this.inputs.AddInputListener(MouseButton.Middle);


        }


        public override bool ExecuteMessage(IMessage message)
        {

            switch (message.Type)
            {
                case MessageType.MouseMove:
                    {

                        MsgMouseMove moveMessage = message as MsgMouseMove;
                        message.TypeCheck(moveMessage);

                        HandleMouseMove(moveMessage.PositionDelta);

                      
                    }
                    break;


                case MessageType.MouseScroll:
                    {
                        MsgMouseScroll scrollWheelMessage = message as MsgMouseScroll;
                        message.TypeCheck(scrollWheelMessage);

                        HandleMouseScroll(scrollWheelMessage.ScrollWheelDelta);


 
                    }

                    break;


                default:

                    return false;

                  

            }


            return true;


            
        }


        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);


            //this.parentEntity.Transform.Position += new Vector3(0,vMove,0);

            // Keep camera's angles and zoom within range
            ProcessAngleRestriction();
            ProcessZoomRestriction();



       

     


            // Start with an initial offset
            Vector3 cameraPosition = new Vector3(0.0f, this.zoom, 0.0f);

            // Rotate camera from its offset to its desired position
            ProcessRotation(ref cameraPosition);


            this.ParentEntity.Transform.Position = (cameraPosition + this.targetPosition);
            // Look at the updated target position
            this.ParentEntity.Transform.LookAt(this.targetPosition);
      


        }


        #region 属性

        /// <summary>
        /// Amount of radians the camera is rotated around the UP axis from the world forward (+Z).
        /// </summary>
        public float HorizontalAngle
        {
            get { return horizontalAngle; }
            set { horizontalAngle = value; }
        }
        private float horizontalAngle = 0.0f;

        /// <summary>
        /// Amount of radians the camera is rotated around the world right axis (X).
        /// </summary>
        public float VerticalAngle
        {
            get { return verticalAngle; }
            set { verticalAngle = value; }
        }
        private float verticalAngle = MathHelper.PiOver2;

        /// <summary>
        /// Lowest value that <see cref="verticalAngle"/> is allowed to go.
        /// </summary>
        private float verticalAngleMin = 0.01f;

        /// <summary>
        /// Highest value that <see cref="verticalAngle"/> is allowed to go.
        /// </summary>
        private float verticalAngleMax = MathHelper.Pi - 0.01f;




        /// <summary>
        /// Lowest value that <see cref="zoom"/> is allowed to go.
        /// </summary>
        public float ZoomMin
        {
            get { return zoomMin; }
            set
            {
                // Zoom can never reach zero, distance must always be positive
                //我们不能让缩放成为负数

                value = MathHelper.Max(0.01f, value);
                zoomMin = value;
                // Re-calculate zoom range whenever the range changes
                zoomRange = zoomMax - zoomMin;
            }
        }
        private float zoomMin = 0.01f;

        /// <summary>
        /// Highest value that <see cref="zoom"/> is allowed to go.
        /// </summary>
        public float ZoomMax
        {
            get { return zoomMax; }
            set
            {
                zoomMax = value;
                // Re-calculate zoom range whenever the range changes
                zoomRange = zoomMax - zoomMin;
            }
        }
        private float zoomMax = 40.0f;

        /// <summary>
        /// Camera's zoom value.
        /// </summary>
        public float Zoom
        {
            get { return zoom; }
            set
            {
                // Make sure zoom is being set within the min-max range
                value = MathHelper.Clamp(value, zoomMin, zoomMax);
                zoom = value;
            }
        }
        private float zoom =2.0f;

        /// <summary>
        /// Difference between the zoomMax and zoomMin
        /// </summary>
        private float zoomRange = 9.99f;

        /// <summary>
        /// Position of parent is stored here for ease of use.
        /// </summary>
        public Vector3 TargetPosition
        {
            set { targetPosition = value; }
        }
        private Vector3 targetPosition = Vector3.Zero;


        #endregion

        /// <summary>
        /// Handles mouse movement
        /// </summary>
        /// <param name="distance">Distance mouse has moved, in each direction (X and Y).</param>
        private void HandleMouseMove(Vector2 distance)
        {
            // Only allow mouse movement to control camera if the left mouse button is being held
            if (this.inputs.IsHeld(MouseButton.Left))
            {

                int invertMod = 1;

              

                this.verticalAngle -= distance.Y * invertMod;

                
                this.horizontalAngle -= distance.X;

               

            }



        }

        /// <summary>
        /// Handles mouse scroll wheel. This camera zooms based on mouse scrolling
        /// </summary>
        /// <param name="distance">Amount mouse scroll wheel has moved</param>
        private void HandleMouseScroll(int distance)
        {

            if (distance > 0)
            {
                this.zoom -= (this.zoomMax - this.zoomMin) * 0.1f;
            }
            else
            {
                this.zoom += (this.zoomMax - this.zoomMin) * 0.1f;
            }

          
        }

        /// <summary>
        /// Process camera's zoom to make sure it is always within acceptable range.
        /// </summary>
        private void ProcessZoomRestriction()
        {
            zoom = MathHelper.Clamp(zoom, zoomMin, zoomMax);
        }

        /// <summary>
        /// Process camera's angles to make sure they're always within acceptable limits.
        /// </summary>
        private void ProcessAngleRestriction()
        {
            // Keep vertical angle within tolerances
            verticalAngle = MathHelper.Clamp(verticalAngle, verticalAngleMin, verticalAngleMax);


            //这里为什么锁定了垂直旋转的角度呢，可能与垂直的某些三角函数有关，尝试一下解锁,发现真的会有问题，等日后慢慢解决吧

            //if (verticalAngle > MathHelper.TwoPi)
            //{
            //    verticalAngle -= MathHelper.TwoPi;
            //}
            //else if (verticalAngle < 0.0f)
            //{
            //    verticalAngle += MathHelper.TwoPi;
            //}





            // Keep vertical angle within PI
            if (horizontalAngle > MathHelper.TwoPi)
            {
                horizontalAngle -= MathHelper.TwoPi;
            }
            else if (horizontalAngle < 0.0f)
            {
                horizontalAngle += MathHelper.TwoPi;
            }
        }



        /// <summary>
        /// Process camera rotation. Each frame the camera actually starts above the target, and then rotates
        /// properly into place.
        /// </summary>
        /// <param name="cameraPosition">Camera's current position</param>
        private void ProcessRotation(ref Vector3 cameraPosition)
        {
            // Rotate vertically
            cameraPosition = Vector3.Transform(cameraPosition, Matrix.CreateRotationX(verticalAngle));

           
            // Rotate horizontally
            cameraPosition = Vector3.Transform(cameraPosition, Matrix.CreateRotationY(horizontalAngle));
        }

    }
}
